package com.hmall.common.config;

import cn.hutool.core.lang.UUID;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.concurrent.ListenableFutureCallback;

@RequiredArgsConstructor//引入了@RequiredArgsConstructor注解，这个注解会为类中的所有final字段生成一个参数的构造函数。
@Configuration
@Slf4j
@ConditionalOnProperty(name = "spring.rabbitmq.listener.simple.retry.enabled", havingValue = "true")
//MQ 的工具类，用于发送消息到MQ，并设置消息的id，然后通过回调函数进行消息的ack确认，如果确认失败，则进行重试。
public class RabbitMqHelper {

    private  final RabbitTemplate rabbitTemplate;

    // 发送消息到MQ
    public void sendMessage(String exchange, String routingKey, Object msg){
        log.info("发送消息到MQ：exchange:" + exchange + ",routingKey:" + routingKey + ",msg:" + msg);
        rabbitTemplate.convertAndSend(exchange, routingKey, msg);
    }
    // 发送延时消息到MQ
    public void sendDelayMessage(String exchange, String routingKey, Object msg, int delay){
        rabbitTemplate.convertAndSend(exchange, routingKey, msg, message -> {
            message.getMessageProperties().setDelay(delay);
            return message;
        });
    }

    // 发送消息到MQ，并设置消息的id，然后通过回调函数进行消息的ack确认，如果确认失败，则进行重试。
    public void sendMessageWithConfirm(String exchange, String routingKey, Object msg, int maxRetries){
        log.debug("准备发送消息，exchange:{}, routingKey:{}, msg:{}", exchange, routingKey, msg);
        //这里消息的id是mq回执的id和后续消费者确认消息的id无关
        CorrelationData data = new CorrelationData(UUID.randomUUID().toString(true));
        data.getFuture().addCallback(new ListenableFutureCallback<CorrelationData.Confirm>() {
            int retryCount;
            @Override
            public void onFailure(Throwable ex) {
                log.error("处理ack回执失败", ex);

            }

            //如果收到 nack（消息未被确认），则会重试发送消息，直到达到最大重试次数。
            @Override
            public void onSuccess(CorrelationData.Confirm result) {
                if(result != null && !result.isAck()){
                    log.debug("消息发送失败，收到nack，已经重试次数{}", retryCount);
                    if(retryCount >= maxRetries){
                        log.error("消息发送重试次数耗尽，发送失败");
                        return;
                    }
                    CorrelationData data = new CorrelationData(UUID.randomUUID().toString(true));
                    data.getFuture().addCallback(this);//this 关键字用于引用当前的 ListenableFutureCallback 实例。
                    rabbitTemplate.convertAndSend(exchange, routingKey, msg, data);
                    retryCount++;
                }
            }
        });
        rabbitTemplate.convertAndSend(exchange, routingKey, msg, data);
    }



}
